package org.aeon.service.impl;

import com.google.gson.Gson;
import io.netty.channel.Channel;
import io.netty.channel.ChannelId;
import io.netty.channel.DefaultChannelId;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.aeon.service.Session;
import org.aeon.utils.RedisPoolUtils;
import redis.clients.jedis.Jedis;

/**
 * @author: MissingAeon
 * @date: 2024/1/26 11:35
 * @description:
 */
@Slf4j
public class SessionRedisImpl implements Session {
    private static final String SUFFIX_CHANNEL = "-channel";
    private static final String SUFFIX_TYPE = "-type";
    private static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    private final Gson gson = new Gson();

    @Override
    public void bind(Channel channel, Long userId) {
        try (final Jedis jedis = RedisPoolUtils.getJedis()) {
            final ChannelId channelId = channel.id();
            jedis.set(userId + SUFFIX_CHANNEL, gson.toJson(channelId));
            jedis.set(gson.toJson(channelId), userId.toString());
            channels.add(channel);
        }
    }

    @Override
    public void unbind(Channel channel) {
        try (final Jedis jedis = RedisPoolUtils.getJedis()) {
            final ChannelId channelId = channel.id();
            final String json = gson.toJson(channelId);
            final String userIdStr = jedis.get(json);
            jedis.del(json);
            jedis.del(userIdStr + SUFFIX_CHANNEL);
            channels.remove(channel);
        }
    }

    @Override
    public Object getAttribute(Channel channel, String name) {
        try (final Jedis jedis = RedisPoolUtils.getJedis()) {
            final ChannelId channelId = channel.id();
            final String json = gson.toJson(channelId);
            if (jedis.hexists(json, name)) {
                final String value = jedis.hget(json, name);
                final String type = jedis.hget(json, name + SUFFIX_TYPE);

                final Gson gson = new Gson();
                return gson.fromJson(value, Class.forName(type));
            }
            return null;
        } catch (ClassNotFoundException e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

    @Override
    public void setAttribute(Channel channel, String name, Object value) {
        try (final Jedis jedis = RedisPoolUtils.getJedis()) {
            final ChannelId channelId = channel.id();
            final String json = gson.toJson(channelId);
            jedis.hset(json, name, gson.toJson(value));
            jedis.hset(json, name + SUFFIX_TYPE, value.getClass().getName());
        }
    }

    @Override
    public Channel getChannel(Long userId) {
        try (final Jedis jedis = RedisPoolUtils.getJedis()) {
            final String key = userId + SUFFIX_CHANNEL;
            if (jedis.exists(key)) {
                final String s = jedis.get(key);
                final ChannelId channelId = gson.fromJson(s, DefaultChannelId.class);
                return channels.find(channelId);
            }
            return null;
        }
    }

    @Override
    public Long getUserId(Channel channel) {
        try (final Jedis jedis = RedisPoolUtils.getJedis()) {
            final String s = jedis.get(gson.toJson(channel.id()));
            return gson.fromJson(s, Long.class);
        }
    }
}
